package model.entity.stats;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import main.Log;
import utilities.*;

public class StatSet implements Archiveable {
	// Instance Variables
	private Map<String, Stat> stats;
	private Map<String, DerivedStat> derivedStats;
	
	
	// Constructor
	/**
	 * Constructor: initializes empty Stat and DerivedStat Lists
	 */
	public StatSet() {
		this.stats = new HashMap<String, Stat>();
		this.derivedStats = new HashMap<String, DerivedStat>();
	}
	
	public void addToBaseValue(double val, String StatType)
	{
		
	}
	
	
	// Getters
	/**
	 * Returns the Stat for the specified name, null otherwise
	 */
	public Stat getStat(String type) {
		return this.stats.get(type);
	}
	
	
	/**
	 * Returns the DerivedStat for the specified name, null otherwise
	 */
	public DerivedStat getDerivedStat(String type) {
		return this.derivedStats.get(type);
	}
	
	public boolean hasStat(String type)
	{
		Stat stat = this.getStat(type);
		if (stat != null) {
			return true;
		} else
			{
			// DerivedStat
			DerivedStat derivedStat = this.getDerivedStat(type);
			if (derivedStat != null) {
				return true;
			}
		}
		
		return false;
	}

	public double getStatVal(String type)
	{ //This can either return a Double with a capital D (and have it automatically convert to a double) and 
	  //then return null at the end of the conditionals OR throw an exception. This solution is probably better
	  //as Double =: null -> would probably just silently return 0. 
		Stat stat = this.getStat(type);
		if (stat != null) {
			return stat.getValue();
		} else
			{
			// DerivedStat
			DerivedStat derivedStat = this.getDerivedStat(type);
			if (derivedStat != null) {
				return derivedStat.getValue();
			}
		}
		Log.out(Log.MODEL, "StatSet: Nonexistant StatType looked for");
		throw new RuntimeException();
		
		//return null;
	}
	
	public Map<String, Stat> getStats() {
		return this.stats;
	}
	
	public Map<String, DerivedStat> getDerivedStats() {
		return this.derivedStats;
	}
	
	// Modifiers
	/**
	 * Adds the Stat to the stats list
	 */
	public void addStat(String key, Stat stat) {
		this.stats.put(key, stat);
	}
	
	/**
	 * Adds the DerivedStat to the derived stats list
	 */
	public void addDerivedStat(String key, DerivedStat stat) {
		this.derivedStats.put(key, stat);
	}
	
	/**
	 * Removes and returns the stat for the specified type, null otherwise
	 */
	public Stat removeStat(String type) {
		return this.stats.remove(type);
	}
	
	/**
	 * Removes and returns the derived stat for the specified type, null
	 * otherwise
	 */
	public DerivedStat removeDerivedStat(String type) {
		return this.derivedStats.remove(type);
	}
	
	// Stat Modifiers
	/**
	 * Adds the stat modifier to the specified stat
	 */
	public void addStatModifier(String type, StatModifier modifier) {
		// Stat
		Stat stat = this.getStat(type);
		if (stat != null) {
			stat.addStatModifier(modifier);
		} else
			{
			// DerivedStat
			DerivedStat derivedStat = this.getDerivedStat(type);
			if (derivedStat != null) {
				derivedStat.addStatModifier(modifier);
			}
		}
	}
	
	public void addConstantModifier(String type, double modifier) {
		// Stat
		Stat stat = this.getStat(type);
		if (stat != null) {
			stat.addStatModifier(new ConstantStatModifier(type, modifier) );
		} else
			{
			// DerivedStat
			DerivedStat derivedStat = this.getDerivedStat(type);
			if (derivedStat != null) {
				derivedStat.addStatModifier(new ConstantStatModifier(type, modifier));
			}
		}
	}
	
	public void addPercentModifier(String type, double modifier) {
		// Stat
		Stat stat = this.getStat(type);
		if (stat != null) {
			stat.addStatModifier(new PercentStatModifier(type, modifier));
		} else
			{
			
			// DerivedStat
			DerivedStat derivedStat = this.getDerivedStat(type);
			if (derivedStat != null) {
				derivedStat.addStatModifier(new PercentStatModifier(type, modifier));
			}
		}
	}
	
	/**
	 * Removes and returns the stat modifier for the specified stat, null
	 * if it does not exist
	 */
	public StatModifier removeStatModifier(String type, String key) {
		// Stat
		Stat stat = this.getStat(type);
		if (stat != null) {
			return stat.removeStatModifier(key);
		}
		
		// DerivedStat
		DerivedStat derivedStat = this.getDerivedStat(type);
		if (derivedStat != null) {
			return derivedStat.removeStatModifier(key);
		}
		
		return null;
	}
	
	// Sample code (demo how everything works)
	public static void main(String args[]) {
		System.out.println("stat test");
		// Create a StatSet
		StatSet stats = new StatSet();
		
		// Create a HP stat with value 100.0
		// new Stat(double value);
		Stat hp = new Stat(100.0, StatType.HP);
		
		// Put HP stat into stat set, using StatType.HP as the key
		// addStat(String key, Stat value)
		stats.addStat(hp.getName(), hp);
		
		// Create a StatModifier (for example, a weapon bonus
		// new ConstantStatModifier(String name, double value)
		StatModifier healthBonus = new ConstantStatModifier("HealthEquipItemBonus", 25.0);
		
		// Add the modifier to the stats set
		// addStatModifier(String statKey, StatModifier modifier)
		stats.addStatModifier(StatType.HP, healthBonus);
		
		// Remove the same modifier
		// removeStatModifier(String statKey, String modifierName)
		StatModifier removedModifier = stats.removeStatModifier(StatType.HP, "HealthEquipItemBonus");
	}
	
	public Archive save() {
		Archive archive = ArchiveFactory.createArchive();
		
		List<String> derivedStatsClassList = new ArrayList<String>();
		List<String> derivedStatsKeys = new ArrayList<String>(this.derivedStats.keySet());
		Collections.sort(derivedStatsKeys);
		List<DerivedStat> derivedStatsValues = new ArrayList<DerivedStat>();
		for (int i = 0; i < derivedStatsKeys.size(); i++) {
			String key = derivedStatsKeys.get(i);
			DerivedStat stat = this.derivedStats.get(key);
			derivedStatsValues.add(stat);
			derivedStatsClassList.add(stat.getClass().getName());
		}
		archive.putAttributeList("derivedStatsClassList", derivedStatsClassList);
		archive.putAttributeList("derivedStatsKeys", derivedStatsKeys);
		archive.putManyRelationship("derivedStatsValues", derivedStatsValues);
		
		List<String> statsClassList = new ArrayList<String>();
		List<String> statsKeys = new ArrayList<String>(this.stats.keySet());
		Collections.sort(statsKeys);
		List<Stat> statsValues = new ArrayList<Stat>();
		for (int i = 0; i < statsKeys.size(); i++) {
			String key = statsKeys.get(i);
			Stat stat = this.stats.get(key);
			statsValues.add(stat);
			statsClassList.add(stat.getClass().getName());
		}
		archive.putAttributeList("statsClassList", statsClassList);
		archive.putAttributeList("statsKeys", statsKeys);
		archive.putManyRelationship("statsValues", statsValues);
		
		return archive;
	}
	
	public void load(Archive archive) {
		List<String> derivedStatsClassList = archive.getAttributeList("derivedStatsClassList");
		List<String> derivedStatsKeys = archive.getAttributeList("derivedStatsKeys");
		List<Archive> derivedStatsValues = archive.getManyRelationship("derivedStatsValues");
		this.derivedStats = new HashMap<String, DerivedStat>();
		for (int i = 0; i < derivedStatsClassList.size(); i++) {
			String key = derivedStatsKeys.get(i);
			DerivedStat stat = (DerivedStat)ModelFactory.getInstance().createInstanceWithClassName(derivedStatsClassList.get(i));
			stat.load(derivedStatsValues.get(i));
			
			this.derivedStats.put(key, stat);
		}
		
		List<String> statsClassList = archive.getAttributeList("statsClassList");
		List<String> statsKeys = archive.getAttributeList("statsKeys");
		List<Archive> statsValues = archive.getManyRelationship("statsValues");
		this.stats = new HashMap<String, Stat>();
		for (int i = 0; i < statsClassList.size(); i++) {
			String key = statsKeys.get(i);
			Stat stat = (Stat)ModelFactory.getInstance().createInstanceWithClassName(statsClassList.get(i));
			stat.load(statsValues.get(i));
			
			this.stats.put(key, stat);
		}
	}
	
}
